﻿using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using Newtonsoft.Json;

namespace Zeus.Results
{
    public class ZResult : IHttpActionResult
    {
        private const string _defaultDateFormatString = "yyyy-MM-dd HH:mm:ss";
        private const string _jsonMediaType = "application/json";

        private readonly IZResultFormatter _zResultFormatter;

        private readonly Encoding _encoding;

        private readonly JsonSerializerSettings _serializerSettings;

        public ZResult(IZResultFormatter zResultFormatter) : this(zResultFormatter, Encoding.UTF8, new JsonSerializerSettings()
        {
            DateFormatString = _defaultDateFormatString
        })
        {

        }

        public ZResult(IZResultFormatter zResultFormatter, JsonSerializerSettings serializerSettings) : this(zResultFormatter, Encoding.UTF8, serializerSettings)
        {

        }

        public ZResult(IZResultFormatter zResultFormatter, Encoding encoding) : this(zResultFormatter, encoding, new JsonSerializerSettings()
        {
            DateFormatString = _defaultDateFormatString
        })
        {

        }

        public ZResult(IZResultFormatter zResultFormatter, Encoding encoding, JsonSerializerSettings serializerSettings)
        {
            _zResultFormatter = zResultFormatter;
            _encoding = Encoding.UTF8;
            _serializerSettings = serializerSettings;
        }


        public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            var response = new HttpResponseMessage(HttpStatusCode.OK);
            var segment = await Serialize();
            response.Headers.Add("Access-Control-Allow-Origin", " *");
            response.Content = new ByteArrayContent(segment.Array, segment.Offset, segment.Count);
            var contentType = new MediaTypeHeaderValue(_jsonMediaType);
            response.Content.Headers.ContentType = contentType;
            contentType.CharSet = _encoding.WebName;

            return response;
        }

        private async Task<ArraySegment<byte>> Serialize()
        {
            JsonSerializer serializer = JsonSerializer.Create(_serializerSettings);
            var buffer = new ArraySegment<byte>();
            using (MemoryStream stream = new MemoryStream())
            {
                const int DefaultStreamWriterBufferSize = 0x400;
                using (var textWriter = new StreamWriter(stream, _encoding,
                    bufferSize: DefaultStreamWriterBufferSize, leaveOpen: true))
                {
                    using (JsonWriter jsonWriter = new JsonTextWriter(textWriter) { CloseOutput = false })
                    {
                        serializer.Serialize(jsonWriter, _zResultFormatter);
                        jsonWriter.Flush();
                    }
                }
                buffer = new ArraySegment<byte>(stream.GetBuffer(), 0, (int)stream.Length);
                stream.Close(); 
            }
            await _zResultFormatter.OnSerialize(buffer);
            return buffer;
        }
    }
}
